﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace lox_z12Class
{
    public abstract class Stmt
    {
        public interface Visitor<R> 
        {
            R visitBlockStmt(Block stmt);
            R visitClassStmt(Class stmt);
            R visitExpressionStmt(Expression stmt);
            R visitFunctionStmt(Function stmt);
            R visitIfStmt(If stmt);
            R visitPrintStmt(Print stmt);
            R visitReturnStmt(Return stmt);
            R visitVarStmt(Var stmt);
            R visitWhileStmt(While stmt);
        }

        // Nested Stmt classes here...
        //> stmt-block
        public class Block : Stmt
        {
            public Block(List<Stmt> statements) { this.statements = statements; }

            public override R accept<R>(Visitor<R> visitor) { return visitor.visitBlockStmt(this); }

            public List<Stmt> statements;
        }
        //< stmt-block
        //> stmt-class
        public class Class : Stmt
        {
            public Class(Token name, List<Stmt.Function> methods)
            {
                this.name = name;
                this.methods = methods;
            }

            public override R accept<R>(Visitor<R> visitor) { return visitor.visitClassStmt(this); }

            public Token name;
            public List<Stmt.Function> methods;
        }
        //< stmt-class
        //> stmt-expression
        public class Expression : Stmt
        {
            public Expression(Expr expression) { this.expression = expression; }

            public override R accept<R>(Visitor<R> visitor) { return visitor.visitExpressionStmt(this); }

            public Expr expression;
        }
        //< stmt-expression
        //> stmt-function
        public class Function : Stmt
        {
            public Function(Token name, List<Token> pParams, List<Stmt> body)
            {
                this.name = name;
                this._params = pParams;
                this.body = body;
            }

            public override R accept<R>(Visitor<R> visitor) { return visitor.visitFunctionStmt(this); }

            public Token name;
            public List<Token> _params;
            public List<Stmt> body;
        }
        //< stmt-function
        //> stmt-if
        public class If : Stmt
        {
            public If(Expr condition, Stmt thenBranch, Stmt elseBranch)
            {
                this.condition = condition;
                this.thenBranch = thenBranch;
                this.elseBranch = elseBranch;
            }

            public override R accept<R>(Visitor<R> visitor) { return visitor.visitIfStmt(this); }

            public Expr condition;
            public Stmt thenBranch;
            public Stmt elseBranch;
        }
        //< stmt-if
        //> stmt-print
        public class Print : Stmt
        {
            public Print(Expr expression) { this.expression = expression; }

            public override R accept<R>(Visitor<R> visitor) { return visitor.visitPrintStmt(this); }

            public Expr expression;
        }
        //< stmt-print
        //> stmt-return
        public class Return : Stmt
        {
            public Return(Token keyword, Expr value)
            {
                this.keyword = keyword;
                this.value = value;
            }

            public override  R accept<R>(Visitor<R> visitor) { return visitor.visitReturnStmt(this); }

            public Token keyword;
            public Expr value;
        }
    //< stmt-return
        //> stmt-var
        public class Var : Stmt
        {
            public Var(Token name, Expr initializer)
            {
                this.name = name;
                this.initializer = initializer;
            }

            public override R accept<R>(Visitor<R> visitor) { return visitor.visitVarStmt(this); }

            public Token name;
            public Expr initializer;
        }
        //< stmt-var
        //> stmt-while
        public class While : Stmt
        {
            public While(Expr condition, Stmt body)
            {
                this.condition = condition;
                this.body = body;
            }

            public override R accept<R>(Visitor<R> visitor) { return visitor.visitWhileStmt(this); }

            public Expr condition;
            public Stmt body;
        }
        //< stmt-while
        public abstract R accept<R>(Visitor<R> visitor);
    }
}
